Added `-[NSArray validateAsPropertyList]` and `-[NSDictionary validateAsPropertyList...
[adiumx.git] / Frameworks / AIUtilities Framework / Source / AIDictionaryAdditions.m
blob7ac4376376c0c96aefe024a5d3053becd84ea86f
1 /*-------------------------------------------------------------------------------------------------------*\
2 | Adium, Copyright (C) 2001-2005, Adam Iser  (adamiser@mac.com | http://www.adiumx.com)                   |
3 \---------------------------------------------------------------------------------------------------------/
4  | This program is free software; you can redistribute it and/or modify it under the terms of the GNU
5  | General Public License as published by the Free Software Foundation; either version 2 of the License,
6  | or (at your option) any later version.
7  |
8  | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9  | the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
10  | Public License for more details.
11  |
12  | You should have received a copy of the GNU General Public License along with this program; if not,
13  | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
14  \------------------------------------------------------------------------------------------------------ */
17     Allows easy access to the elements of a dictionary
20 #import "AIDictionaryAdditions.h"
21 #import "AIFileManagerAdditions.h"
23 @implementation NSDictionary (AIDictionaryAdditions)
25 // Returns a dictionary from the owners bundle with the specified name
26 + (NSDictionary *)dictionaryNamed:(NSString *)name forClass:(Class)inClass
28     NSBundle            *ownerBundle;
29     NSString            *dictPath;
30     NSDictionary        *dict;
32     //Get the bundle
33     ownerBundle = [NSBundle bundleForClass:inClass];
35     //Open the image
36     dictPath = [ownerBundle pathForResource:name ofType:@"plist"];    
37     dict = [NSDictionary dictionaryWithContentsOfFile:dictPath];
39     return dict;
42 // returns the dictionary from the specified path
43 + (NSDictionary *)dictionaryAtPath:(NSString *)path withName:(NSString *)name create:(BOOL)create
45     NSParameterAssert(path != nil); NSParameterAssert([path length] != 0);
46     NSParameterAssert(name != nil); NSParameterAssert([name length] != 0);
47         
48         NSDictionary    *dictionary = [NSDictionary dictionaryWithContentsOfFile:[[path stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"plist"]];
50         if (!dictionary && create) dictionary = [NSDictionary dictionary];
52     return dictionary;
55 - (BOOL)validateAsPropertyList
57         BOOL validated = YES;
58         NSEnumerator *enumerator = [self keyEnumerator];
59         NSString         *key;
60         while ((key = [enumerator nextObject])) {
61                 if (![key isKindOfClass:[NSString class]]) {
62                         NSLog(@"** Dictionary failed validation: %@: Key %@ is a %@ but must be an NSString",
63                                   self, key, NSStringFromClass([key class]));
64                         validated = NO;
65                 }
67                 id value = [self objectForKey:key];
68                 Class valueClass = [value class];
69                 if (![value isKindOfClass:[NSString class]] &&
70                         ![value isKindOfClass:[NSData class]] &&
71                         ![value isKindOfClass:[NSNumber class]] &&
72                         ![value isKindOfClass:[NSArray class]] &&
73                         ![value isKindOfClass:[NSDictionary class]] &&
74                         ![value isKindOfClass:[NSDate class]]) {
75                         NSLog(@"** Dictionary failed validation: %@: Value %@ is a %@ but must be a string, data, number, array, dictionary, or date",
76                                   self, value, NSStringFromClass(valueClass));
77                         validated = NO;
78                 }
79                 
80                 if ([value isKindOfClass:[NSArray class]] ||[value isKindOfClass:[NSDictionary class]]) {
81                         BOOL successOfValue = [value validateAsPropertyList];
82                         if (validated) validated = successOfValue;
83                 }
84         }
86 return validated;
89 // saves this dictionary to the specified path
90 - (BOOL)writeToPath:(NSString *)path withName:(NSString *)name
92         NSParameterAssert(path != nil); NSParameterAssert([path length] != 0);
93     NSParameterAssert(name != nil); NSParameterAssert([name length] != 0);
95         [[NSFileManager defaultManager] createDirectoriesForPath:path]; //make sure the path exists
96         
97         NSData *plistData;
98         NSString *retainedError = nil;
99         plistData = [NSPropertyListSerialization dataFromPropertyList:self
100                                                                                                                    format:NSPropertyListBinaryFormat_v1_0
101                                                                                                  errorDescription:&retainedError];
102         if (plistData) {
103                 BOOL success = [plistData writeToFile:[[path stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"plist"]
104                                                                   atomically:YES];
105                 if (!success)
106                         NSLog(@"%s: Error writing path %@, name %@ (%@)", __PRETTY_FUNCTION__, path, name, [[path stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"plist"]);
108                 return success;
109         } else {                
110                 NSLog(@"%s: Could not serialize. Error: \"%@\".", retainedError);
111                 [self validateAsPropertyList];
113                 if (retainedError) [retainedError release];
115                 return NO;
116         }
119 - (NSDictionary *)dictionaryByTranslating:(NSDictionary *)translation adding:(NSDictionary *)addition removing:(NSSet *)removal
121         NSDictionary *result;
123         //only do work if we have work to do.
124         if (translation || addition || removal) {
125                 NSMutableDictionary *mutable = [self mutableCopy];
127                 [mutable translate:translation add:addition remove:removal];
129                 result = [[self class] dictionaryWithDictionary:mutable];
130                 [mutable release];
131         } else {
132                 result = [[self retain] autorelease];
133         }
135         return result;
138 - (NSSet *)allKeysSet {
139         return [NSSet setWithArray:[self allKeys]];
141 - (NSMutableSet *)allKeysMutableSet {
142         return [NSMutableSet setWithArray:[self allKeys]];
145 - (void)compareWithPriorDictionary:(NSDictionary *)other
146                       getAddedKeys:(out NSSet **)outAddedKeys
147                     getRemovedKeys:(out NSSet **)outRemovedKeys
148                 includeChangedKeys:(BOOL)flag
150         if (!other) {
151                 if (outAddedKeys) *outAddedKeys = [self allKeysSet];
152                 if (outRemovedKeys) *outRemovedKeys = [NSSet set];
153         } else {
154                 NSMutableSet *addedKeys = nil;
155                 if (outAddedKeys) {
156                         unsigned capacity = [self count];
157                         if (flag) capacity += [other count];
158                         addedKeys = [NSMutableSet setWithCapacity:capacity];
159                 }
161                 NSMutableSet *removedKeys = nil;
162                 if (outRemovedKeys) {
163                         unsigned capacity = [other count];
164                         if (flag) capacity += [self count];
165                         removedKeys = [NSMutableSet setWithCapacity:capacity];
166                 }
168                 NSEnumerator *keysEnum = [self keyEnumerator];
169                 NSString *key;
170                 while ((key = [keysEnum nextObject])) {
171                         id otherObj = [other objectForKey:key];
172                         if (!otherObj) {
173                                 //Not in other, but is in self: Added.
174                                 [addedKeys addObject:key];
175                         } else if(flag && ![otherObj isEqual:[self objectForKey:key]]) {
176                                 //Exists in both, objects are not equal, and flag is not NO: Different, and we should put the key in both sets to indicate this.
177                                 [addedKeys addObject:key];
178                                 [removedKeys addObject:key];
179                         }
180                 }
181                 if (outAddedKeys) *outAddedKeys = [NSSet setWithSet:addedKeys];
183                 if (outRemovedKeys) {
184                         keysEnum = [other keyEnumerator];
185                         while ((key = [keysEnum nextObject])) {
186                                 if (![self objectForKey:key]) {
187                                         //In other, but not in self: Removed.
188                                         [removedKeys addObject:key];
189                                 }
190                         }
192                         *outRemovedKeys = [NSSet setWithSet:removedKeys];
193                 }
194         }
197 - (NSDictionary *)dictionaryWithIntersectionWithSetOfKeys:(NSSet *)keys
199         NSMutableDictionary *mutableSelf = [self mutableCopy];
200         [mutableSelf intersectSetOfKeys:keys];
201         NSDictionary *result = [NSDictionary dictionaryWithDictionary:mutableSelf];
202         [mutableSelf release];
203         return result;
205 - (NSDictionary *)dictionaryWithDifferenceWithSetOfKeys:(NSSet *)keys
207         NSMutableDictionary *mutableSelf = [self mutableCopy];
208         [mutableSelf minusSetOfKeys:keys];
209         NSDictionary *result = [NSDictionary dictionaryWithDictionary:mutableSelf];
210         [mutableSelf release];
211         return result;
214 - (NSString *)CSSString
216         NSMutableArray *properties = [NSMutableArray arrayWithCapacity:[self count]];
218         NSEnumerator *keysEnum = [self keyEnumerator];
219         NSString *key;
220         while ((key = [keysEnum nextObject])) {
221                 [properties addObject:[NSString stringWithFormat:@"%@: %@;", key, [self objectForKey:key]]];
222         }
224         return [properties componentsJoinedByString:@" "];
227 @end
229 @implementation NSMutableDictionary (AIDictionaryAdditions)
231 // returns the dictionary from the specified path
232 + (NSMutableDictionary *)dictionaryAtPath:(NSString *)path withName:(NSString *)name create:(BOOL)create
234         NSMutableDictionary     *dictionary;
235         
236     NSParameterAssert(path != nil); NSParameterAssert([path length] != 0);
237     NSParameterAssert(name != nil); NSParameterAssert([name length] != 0);
238         
239         NSData *plistData;
240         NSString *error;
241         
242         plistData = [[NSData alloc] initWithContentsOfFile:[[path stringByAppendingPathComponent:name] stringByAppendingPathExtension:@"plist"]];
243         
244         dictionary = [NSPropertyListSerialization propertyListFromData:plistData
245                                                                                                   mutabilityOption:NSPropertyListMutableContainers
246                                                                                                                         format:NULL
247                                                                                                   errorDescription:&error];
248         [plistData release];
250         if (!dictionary && create) dictionary = [NSMutableDictionary dictionary];
251         
252     return dictionary;
256 - (void)translate:(NSDictionary *)translation add:(NSDictionary *)addition remove:(NSSet *)removal
258         //only do work if we have work to do.
259         if (translation || addition || removal) {
260                 NSDictionary *selfCopy = [self copy];
261                 NSEnumerator *keyEnum = [selfCopy keyEnumerator];
262                 NSString *key;
264                 while ((key = [keyEnum nextObject])) {
265                         NSString *newKey = [translation objectForKey:key];
266                         if (newKey) {
267                                 [self setObject:[selfCopy objectForKey:key] forKey:newKey];
268                         }
270                         if (removal && [removal containsObject:key]) {
271                                 [self removeObjectForKey:key];
272                         }
273                 }
275                 [selfCopy release];
277                 //Add items that aren't in the removal set.
278                 keyEnum = [addition keyEnumerator];
279                 while ((key = [keyEnum nextObject])) {
280                         if (!(removal && [removal containsObject:key])) {
281                                 [self setObject:[addition objectForKey:key] forKey:key];
282                         }
283                 }
284         }
287 - (void)intersectSetOfKeys:(NSSet *)keys
289         NSMutableArray *keysToRemove = [NSMutableArray array];
290         NSEnumerator *myKeysEnum = [self keyEnumerator];
291         NSString *key;
292         while ((key = [myKeysEnum nextObject])) {
293                 if (![keys containsObject:key]) {
294                         [keysToRemove addObject:key];
295                 }
296         }
297         NSEnumerator *deadKeysEnum = [keysToRemove objectEnumerator];
298         while ((key = [deadKeysEnum nextObject])) {
299                 [self removeObjectForKey:key];
300         }
302 - (void)minusSetOfKeys:(NSSet *)keys
304         NSEnumerator *keysEnum = [keys objectEnumerator];
305         NSString *key;
306         while ((key = [keysEnum nextObject])) {
307                 [self removeObjectForKey:key];
308         }
311 @end